#include "Connection.h"
#include <iostream>
#include "Vector.h"
#include "Server.h"
#include "BodyInfo.h"
#include "Game.h"

#pragma warning (disable : 4482)

void Connection::start(unsigned int id, int port)
{
	
	m_Id = id;

	init(port);

	m_hSend = (HANDLE)_beginthreadex(NULL, 1024*1024, threadSend, 
										this, 0, NULL);

	m_hReceive = (HANDLE)_beginthreadex(NULL, 1024*1024, threadReceive, 
										this, 0, NULL);
}

void Connection::init(int port)
{
	sockaddr_in peer;
	peer.sin_family = AF_INET;
	peer.sin_port = htons(port);	// start tcp connection on port specified by server
	peer.sin_addr.S_un.S_addr = htonl( INADDR_ANY );

	closesocket(m_socket);
	// Create listening socket
	m_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (m_socket==INVALID_SOCKET) {
		std::cerr << "Create socket failed" << std::endl;
	} else if (bind(m_socket, (sockaddr *)&peer, sizeof(peer))==SOCKET_ERROR) {
		std::cerr << "Bind failed with " << WSAGetLastError()  << std::endl;
	}
	
	std::cout << "Listening for TCP connection" << std::endl;

	if (listen(m_socket, 5)==SOCKET_ERROR) {
		std::cerr << "Listen failed with " << WSAGetLastError()  << std::endl;
	} else {

		std::cout << "TCP Connection received" << std::endl;

		sockaddr_in clientAddr;
		int addrSize = sizeof(clientAddr);

		// Create transfer socket
		m_socket = accept(m_socket, (sockaddr*)&clientAddr, &addrSize);
		if (m_socket==INVALID_SOCKET) {
			std::cerr << "Accept failed with " << WSAGetLastError() << std::endl;
		}

		std::cout << "TCP connection accepted at: " << port << std::endl;

		
		mAddress = clientAddr.sin_addr;
		mPort = port;
		mConnected = true;
	}
}

in_addr&		Connection::address()
{
	return mAddress;
}


void Connection::waitForTermination()
{
	WaitForSingleObject(m_hSend, INFINITE);
	WaitForSingleObject(m_hReceive, INFINITE);
	CloseHandle(m_hSend);
	CloseHandle(m_hReceive);
	m_hSend = m_hReceive = 0;
}

void Connection::startTimer()
{
	LARGE_INTEGER li;
    QueryPerformanceFrequency(&li);
    freq = static_cast<double>(li.QuadPart);

    QueryPerformanceCounter(&li);
    tmr_start = li.QuadPart;
}

double Connection::getDeltaTime()
{
	 LARGE_INTEGER li;

    QueryPerformanceCounter(&li);
    __int64 current = li.QuadPart;

    double dt = static_cast<double>(current - tmr_start) / freq;

    tmr_start = current;

    return dt;
}

double Connection::getTime()
{
	 LARGE_INTEGER li;

    QueryPerformanceCounter(&li);
    __int64 current = li.QuadPart;

    return (double)(current/freq);
}


bool Connection::updateClient()
{
	const int statsBufferSize = 7 * sizeof(float);
	float statsBuffer[7];
	int iTrue = 1;
	int iFalse = 0;

	// Header + message for rope
	if (m_server->m_game->activeRope()) {
		Message msgRope;
		msgRope.header.type = MsgType::MSG_SPRINGINFO;
		msgRope.header.length = m_server->m_game->getRopePointsCount() * sizeof(Vector);
		msgRope.data = (char*)m_server->m_game->getRopeData();

		// send message header
		if (send(m_socket, (char*)&msgRope.header, sizeof(msgRope.header), 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Header Send failed with " << WSAGetLastError() << std::endl;
		}
		// send message data
		if (send(m_socket, (char*)msgRope.data, msgRope.header.length, 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Data Send failed with " << WSAGetLastError() << std::endl;
		}

		// rope = active message
		ZeroMemory(&msgRope, sizeof(msgRope));
		msgRope.header.type = MsgType::MSG_SPRINGACTIVE;
		msgRope.header.length = sizeof(int);
		msgRope.data = (void*)&iTrue;

		// send message header
		if (send(m_socket, (char*)&msgRope.header, sizeof(msgRope.header), 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Header Send failed with " << WSAGetLastError() << std::endl;
		}
		// send message data
		if (send(m_socket, (char*)msgRope.data, msgRope.header.length, 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Data Send failed with " << WSAGetLastError() << std::endl;
		}

	}
	else
	{
		// rope = inactive message
		Message msgRope;
		ZeroMemory(&msgRope, sizeof(msgRope));
		msgRope.header.type = MsgType::MSG_SPRINGACTIVE;
		msgRope.header.length = sizeof(int);
		msgRope.data = (void*)&iFalse;

		// send message header
		if (send(m_socket, (char*)&msgRope.header, sizeof(msgRope.header), 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Header Send failed with " << WSAGetLastError() << std::endl;
		}
		// send message data
		if (send(m_socket, (char*)msgRope.data, msgRope.header.length, 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Data Send failed with " << WSAGetLastError() << std::endl;
		}

	}
	//----------------------------------------


	// Header + message for MSG_CIRCLE_INFO
	Message msg;
	msg.header.type = MsgType::MSG_BodyInfo;
	msg.header.length = m_server->m_bufferSize;
	msg.data = (char*)m_server->m_sendBuffer;

	if (msg.header.length != 0) {
		// send message header
		if (send(m_socket, (char*)&msg.header, sizeof(msg.header), 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Header Send failed with " << WSAGetLastError() << std::endl;
		}
		// send message data
		if (send(m_socket, (char*)msg.data, msg.header.length, 0)==SOCKET_ERROR) {
			return false;
			std::cerr << "Data Send failed with " << WSAGetLastError() << std::endl;
		}
	}
	//----------------------------------------
			
	// Header + message for stats
	ZeroMemory(&msg, sizeof(msg));
	msg.header.type = MsgType::MSG_STATS;
	msg.header.length = statsBufferSize;
	msg.data = (char*)statsBuffer;

	// stats buffer : totalobjects, airForce, elasticity, friction, freq,freq,freq
	float  v[7];
	v[0] = (float)m_server->m_game->getTotalObjects();
	v[1] =	m_server->m_game->getAirjetForce();
	v[2] = m_server->m_game->getElasticity();
	v[3] = m_server->m_game->getFriction();
	v[4] = m_server->m_game->graphicsFreq();
	v[5] = m_server->m_game->networkFreq();
	v[6] = m_server->m_game->physicsFreq();
	memcpy((void*)statsBuffer, (void*)v, sizeof(v));


	// send message header
	if (send(m_socket, (char*)&msg.header, sizeof(msg.header), 0)==SOCKET_ERROR) {
		return false;
		std::cerr << "Header Send failed with " << WSAGetLastError() << std::endl;
	}
	// send message data
	if (send(m_socket, (char*)msg.data, msg.header.length, 0)==SOCKET_ERROR) {
		return false;
		std::cerr << "Data Send failed with " << WSAGetLastError() << std::endl;
	}
	return true;
}

int Connection::sendData()
{
	double freqNetwork = m_server->m_game->networkFreq();
	double delta = 1.0f / (double)freqNetwork;

	double d0, d1, elapsedTime=0;
	int counter = 0;
	double timer = 0;
	double currentTime = 0;
	double avgElapsedTime = 0.0;
	double baseTime = getTime();

	startTimer();


	while (!m_bTerminate)
	{
		currentTime = getTime() - baseTime;
			
		if (!mConnected) {
			SwitchToThread();
		}

		if (currentTime >= delta*counter) 
		{
			d0 = getTime();
				if (mConnected) // try to send only if connection alive
					mConnected = updateClient();
			d1 = getTime();
	
			elapsedTime = d1-d0;
			counter++;

			// Things to be checked after execution of each loop ------------
		}

		// sleep remaining time
		if (elapsedTime < delta) { } 
		else {
			//std::cout << "elapsedTime > delta" << endl;		
		}
		
		avgElapsedTime += elapsedTime;
		timer += getDeltaTime();

		if (!mConnected) {
			close();
			m_server->connectionError(m_Id);
		}

		// Things to do per Second -----------------------------
		if (timer >= 1.0) {
			timer = 0.0;

			// average elapsedTime
			avgElapsedTime /= (double)freqNetwork;
			// get new value for baseTime
			baseTime = getTime();


			// get current value for frequency and update delta
			freqNetwork = m_server->m_game->networkFreq();
			delta = 1.0f / (double)freqNetwork;

			//std::cout << "Network fps : " << counter << std::endl;

			avgElapsedTime = 0;
			counter = 0;
		}
		

	}

	return 0;
}


bool Connection::receive(SOCKET socket, char* data, int size)
{
	int receivedBytes = 0;
	while(receivedBytes < size){
		int read = recv(socket, data+receivedBytes, size-receivedBytes, 0);
		if (read == SOCKET_ERROR)  {
			return false;
			std::cerr << "Receive failed with " << WSAGetLastError()  << std::endl;
		}

		receivedBytes += read;
	}
	return true;
}

int Connection::receiveData()
{
	char buffer[100];
	ZeroMemory(buffer, sizeof(buffer));

	Message msg;

	char data[500];

	while (!m_bTerminate)
	{
		if (!mConnected) {
			SwitchToThread();
			continue;
		}

		// read message header
		mConnected = receive(m_socket, (char*)&msg.header, sizeof(msg.header));

		// read message data
		mConnected = receive(m_socket, (char*)data, msg.header.length);
		
		if (!mConnected) {
			close();
			continue;
		}

		if (msg.header.type == MsgType::MSG_NEWCIRCLE)
		{
			Vector p = *((Vector*)data);
			m_server->m_game->AddCommand(CmdType::CMD_NEWCIRCLE, p.GetX(), p.GetY());
			m_server->m_bufferSize += sizeof(BodyInfo);
		}
		else if (msg.header.type == MsgType::MSG_UPD_ELASTICITY) {
			float v = *((float*)data);
			m_server->m_game->setElasticity(v);
		}
		else if (msg.header.type == MsgType::MSG_UPD_FRICTION) {
			float v = *((float*)data);
			m_server->m_game->setFriction(v);
		}
		else if (msg.header.type == MsgType::MSG_UPD_GRAPHICSFREQ) {
			float v = *((float*)data);
			m_server->m_game->graphicsFreq(v);
		}
		else if (msg.header.type == MsgType::MSG_UPD_NETWORK_FREQ) {
			float v = *((float*)data);
			m_server->m_game->networkFreq(v);
		}
		else if (msg.header.type == MsgType::MSG_UPD_PHYSICS_FREQ) {
			float v = *((float*)data);
			m_server->m_game->physicsFreq(v);
		}
		else if (msg.header.type == MsgType::MSG_NEWAIRJET) {
			Vector airjet[2];
			memcpy((void*)airjet, data, sizeof(airjet));
			m_server->m_game->setAirjet(airjet[0], airjet[1]);
		}
		else if (msg.header.type == MsgType::MSG_UPD_PAUSE) {
			m_server->m_game->pause();
		}
		else if (msg.header.type == MsgType::MSG_TOGGLESPRING) {
			m_server->m_game->AddCommand(CmdType::CMD_TOGGLESPRING);
		}
		else if (msg.header.type == MsgType::MSG_MORECIRCLES) {
			m_server->m_bufferSize += sizeof(BodyInfo) * 8;
			m_server->m_game->AddCommand(CmdType::CMD_MANYCIRCLES);
		}
	}
	return 12;
}


void Connection::restart(int port)
{
	bLocked = true;
	init(port);
	bLocked = false;
}

void Connection::close()
{
	static int closed = false;

	if (!closed) {
		int err = closesocket(m_socket);
		closed = true;
	}
	
}
